%{
#include <stdio.h>
#include <stdlib.h>
#include "hash.h"
#include "astree.h"

#include "genco.c"
#include "asm.c"

extern FILE *yyout;
extern int lineNumber;
%}

%token KW_WORD KW_BOOL KW_BYTE KW_IF KW_THEN KW_ELSE KW_LOOP KW_INPUT KW_RETURN KW_OUTPUT OPERATOR_LE OPERATOR_GE  OPERATOR_EQ OPERATOR_NE OPERATOR_AND OPERATOR_OR TOKEN_ERROR

%token <symbol> TK_IDENTIFIER LIT_INTEGER LIT_FALSE LIT_TRUE LIT_CHAR LIT_STRING

%type <node> program type lit idVal globalVar lstInitVec ubiqVar localVarsLoop parameters function lstPrint lstPrintVal cmd cmdCond cmdList block lstParamFuncCall expr identif vecIndex


%left OPERATOR_AND OPERATOR_OR
%left '+' '-'
%left '*' '/'

%nonassoc  OPERATOR_GE OPERATOR_LE OPERATOR_EQ OPERATOR_NE  '<' '>'
%nonassoc DEREF
%nonassoc IFX
%nonassoc KW_ELSE

%union {
    struct node* node;
	row  *symbol;
	int   lineNumber;
}


%%

root: program { checkDefines($1,1,0); checkUndefined($1,1); fprintf(stderr,"^\n"); checkNature($1); fprintf(stderr,"'\n"); fprintf(stderr,"-\n"); checkTypes($1,NULL);  checkFunctionReturnType($1); checkFunctionParameters($1); fprintf(stderr,"\n ===== TACS ===== \n");
	TAC *code =generateCode($1); TAC* code1 = tac_reverse(code); tac_print_list(code1);
	assembleCode(code1, stderr);
	}
    ;

program: program function   { $$ = createNode(AST_LST_GBL_DEF,0,$1,$2,0,0, lineNumber); }
	|    /* empty */        { $$ = 0; }
	|    program globalVar  { $$ = createNode(AST_LST_GBL_DEF,0,$1,$2,0,0, lineNumber); }
	;

type: KW_BOOL { $$ = createNode(TYPE_BOOL,0,0,0,0,0, lineNumber); }
    | KW_WORD { $$ = createNode(TYPE_WORD,0,0,0,0,0, lineNumber); }
    | KW_BYTE { $$ = createNode(TYPE_BYTE,0,0,0,0,0, lineNumber); }
    ;  

lit:  LIT_INTEGER  { $$ = createNode(AST_LITERAL,yylval.symbol,0,0,0,0, lineNumber); }
    | LIT_FALSE    { $$ = createNode(AST_LITERAL,yylval.symbol,0,0,0,0, lineNumber); }
    | LIT_TRUE     { $$ = createNode(AST_LITERAL,yylval.symbol,0,0,0,0, lineNumber); }
    | LIT_CHAR     { $$ = createNode(AST_LITERAL,yylval.symbol,0,0,0,0, lineNumber); }
    ;

identif: TK_IDENTIFIER { $$ = createNode(AST_IDENTIFIER,yylval.symbol,0,0,0,0, lineNumber); }
    ;

vecIndex: LIT_INTEGER  { $$ = createNode(AST_LITERAL,yylval.symbol,0,0,0,0, lineNumber); }
    ;

idVal: type identif { $$ = createNode(AST_ID_VAL,0,$1,$2,0,0, lineNumber); } 
	;

globalVar: idVal ':' lit  ';'                           { $$ = createNode(AST_DEF_GLOBAL_VAR,0,$1,0 ,$3,0, lineNumber); }
    |      type '$'identif ':' lit ';'               	{ $$ = createNode(AST_DEF_GLOBAL_VAR,0,$1,$3,$5,0, lineNumber);}
	|	   idVal '['vecIndex']' ';'                 	{ $$ = createNode(AST_DEF_GLOBAL_VAR,0,$1,0 ,0, $3, lineNumber); }
	|      idVal '['vecIndex']' ':' lstInitVec ';'  	{ $$ = createNode(AST_DEF_GLOBAL_VAR,0,$1,0 ,$6,$3, lineNumber); }
	;

lstInitVec: lstInitVec lit { $$ = createNode(AST_LST_INIT_VEC ,0,$1,$2,0,0, lineNumber); }
	|       lit
	; 


ubiqVar: idVal ':' lit                  { $$ = createNode(AST_DEF_UBIQ_VAR,0,$1,0,$3,0, lineNumber); }
	|    type '$'identif ':' lit  { $$ = createNode(AST_DEF_UBIQ_VAR,0,$1,$3,$5,0, lineNumber); }
	;


localVarsLoop: localVarsLoop ubiqVar ';' { $$ = createNode(AST_LST_LOCAL_VAR,0,$1,$2,0,0, lineNumber); } 
	|		   ubiqVar ';'
	;  

parameters: parameters ',' idVal { $$ = createNode(AST_LST_PARAM_FUNC,0,$1,$3,0,0, lineNumber); }
	|       idVal				
	;

function: idVal '(' parameters ')'  localVarsLoop block {$$ = createNode(AST_DEF_FUNC,0,$1,$3,$5,$6, lineNumber);}
	|	  idVal '('             ')' localVarsLoop block {$$ = createNode(AST_DEF_FUNC,0,$1,0,$4,$5, lineNumber);}
	|     idVal '(' parameters ')'    block             {$$ = createNode(AST_DEF_FUNC,0,$1,$3,0,$5, lineNumber);}
	|	  idVal '('             ')'   block             {$$ = createNode(AST_DEF_FUNC,0,$1,0,0,$4, lineNumber);}
	;


lstPrint: lstPrint ',' lstPrintVal { $$ = createNode(AST_LST_PRINT_VAL ,0,$1,$3,0,0, lineNumber); }
	|     lstPrintVal              { $$ = createNode(AST_LST_PRINT_VAL ,0,0,$1,0,0, lineNumber);  }
	;

lstPrintVal: expr 
	|        LIT_STRING { $$ = createNode(AST_LITERAL,yylval.symbol,0,0,0,0, lineNumber); }
	;


cmd:  identif '=' expr                 { $$ = createNode(AST_CMD_ATTR,0,$1,$3,0,0, lineNumber); }
	| identif '[' expr ']' '=' expr    { node *idVect = createNode(AST_IDENTIFIER,0,$1,$3,0,0, lineNumber); $$ = createNode(AST_CMD_ATTR,0,idVect,$6,0,0, lineNumber); }
	| KW_INPUT  identif                { $$ = createNode(AST_CMD_INPUT,0,$2,0,0,0, lineNumber); }           
	| KW_OUTPUT lstPrint                 { $$ = createNode(AST_CMD_OUTPUT,0,$2,0,0,0, lineNumber); }
	| KW_RETURN expr                         { $$ = createNode(AST_CMD_RETURN,0,$2,0,0,0, lineNumber); }
	| KW_IF '(' expr ')' KW_THEN cmd cmdCond { $$ = createNode(AST_CMD_IF,0,$3,$6,$7,0, lineNumber); }
	| KW_LOOP '(' expr ')' cmd               { $$ = createNode(AST_CMD_LOOP,0,$3,$5,0,0, lineNumber); }
	
	| identif '(' lstParamFuncCall ')' { $$ = createNode(AST_CMD_FUNCTION_CALL,0,$1,$3,0,0, lineNumber); }
	| identif '(' ')'                  { $$ = createNode(AST_CMD_FUNCTION_CALL,0,$1,0,0,0, lineNumber); }
	
	| block                                  { $$ = createNode(AST_CMD_BLOCK,0,$1,0,0,0, lineNumber); }
	;
	
cmdCond: /* empty */              %prec IFX  { $$ = 0; }
	|    KW_ELSE cmd                         { $$ = $2; }
	;


cmdList: cmd ';'         { $$ = createNode(AST_LST_CMD,0,0,$1,0,0, lineNumber); }
	|    cmdList cmd ';' { $$ = createNode(AST_LST_CMD,0,$1,$2,0,0, lineNumber); }
	;

block: '{'         '}' { $$ = 0; }
	|  '{' cmdList '}' { $$ = $2; }
	;

lstParamFuncCall: expr                      { $$ = createNode(AST_LST_PARAM_FUNC_CALL,0,0 ,$1,0,0, lineNumber); }
	|             lstParamFuncCall ',' expr { $$ = createNode(AST_LST_PARAM_FUNC_CALL,0,$1,$3,0,0, lineNumber); }
	;


expr: '(' expr ')'                      { $$ = createNode(AST_PAREN,0,$2,0,0,0, lineNumber); }
	| identif '(' lstParamFuncCall ')'  { $$ = createNode(AST_EXPR_FUNCTION_CALL,0,$1,$3,0,0, lineNumber); }
	| identif '(' ')'                   { $$ = createNode(AST_EXPR_FUNCTION_CALL,0,$1,0,0,0, lineNumber); }

	| expr OPERATOR_LE expr  { $$ = createNode(AST_OP_LE,0,$1,$3,0,0, lineNumber); }
	| expr OPERATOR_GE expr  { $$ = createNode(AST_OP_GE,0,$1,$3,0,0, lineNumber); }
	| expr OPERATOR_EQ expr  { $$ = createNode(AST_OP_EQ,0,$1,$3,0,0, lineNumber); }
	| expr OPERATOR_NE expr  { $$ = createNode(AST_OP_NE,0,$1,$3,0,0, lineNumber); }
	| expr OPERATOR_AND expr { $$ = createNode(AST_OP_AND,0,$1,$3,0,0, lineNumber); }
	| expr OPERATOR_OR expr  { $$ = createNode(AST_OP_OR,0,$1,$3,0,0, lineNumber); }
	| expr     '>'     expr  { $$ = createNode(AST_OP_G,0,$1,$3,0,0, lineNumber); }
	| expr     '<'     expr  { $$ = createNode(AST_OP_L,0,$1,$3,0,0, lineNumber); }
	
	| '*'expr %prec DEREF    { $$ = createNode(AST_OP_DEREF,0,$2,0,0,0, lineNumber); }
	| '&'expr %prec DEREF    { $$ = createNode(AST_OP_REF,0,$2,0,0,0, lineNumber); }

	| expr '+' expr          { $$ = createNode(AST_OP_ADD,0,$1,$3,0,0, lineNumber); }
	| expr '-' expr          { $$ = createNode(AST_OP_SUB,0,$1,$3,0,0, lineNumber); }
	| expr '/' expr          { $$ = createNode(AST_OP_DIV,0,$1,$3,0,0, lineNumber); }
	| expr '*' expr          { $$ = createNode(AST_OP_MUL,0,$1,$3,0,0, lineNumber); }
	
	| identif                        { $$ = createNode(AST_IDENTIFIER,yylval.symbol,0,0,0,0, lineNumber); }
	| identif '['expr']'    { $$ = createNode(AST_IDENTIFIER,0,$1,$3,0,0, lineNumber); }
	| lit                                  { $$ = $1; }
	;


%%

/* Added because panther doesn't have liby.a installed. */
int yyerror (char *msg) {
	fprintf (stderr, "YACC: %s at line %d\n", msg,lineNumber);
	exit(3);
	}
